package com.github.ylt.protocol.http.server.exchange;

import com.github.ylt.conf.Configure;
import com.github.ylt.protocol.event.Event;
import com.github.ylt.protocol.event.HttpEvent;
import com.github.ylt.protocol.http.*;
import com.github.ylt.protocol.http.server.ServerImpl;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Exchange {
    private Headers requestHeaders, responseHeaders;
    private Request request;
    private String method;
    private boolean writefinished;
    private URI uri;
    private HttpConnection connection;
    private long requestContentLength;
    private long responseContentLength;
    private InputStream rawInputStream;
    private OutputStream rawOutputStream;
    private Thread thread;
    private boolean close;
    private boolean closed;
    private boolean http10 = false;
    private ServerImpl server;
    private OutputStream tmpOutputStream;
    private InputStream tmpInputStream;
    private static final List<String> requestType = List.of("HEAD", "GET", "POST", "PUT", "DELETE","NOTIFY","SUBSCRIBE","UNSUBSCRIBE");
    private static final DateTimeFormatter FORMATTER;
    private int code = -1;
    private final Pattern startLinePattern = Pattern.compile("(\\w+) \\/(.+?) (HTTP/[12]\\.\\d+)");
    static {
        String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
        FORMATTER = DateTimeFormatter.ofPattern(pattern, Locale.US)
                .withZone(ZoneId.of("GMT"));
    }

    public Exchange(String method, URI url, int requestContentLength, Request request, HttpConnection connection) throws IOException {
        this.method = method;
        this.request = request;
        this.connection = connection;
        this.requestContentLength = requestContentLength;
        this.requestHeaders = Headers.of(request.getHeaders());
        this.responseHeaders = new Headers();
        this.rawInputStream = request.getInputStream();
        this.rawOutputStream = request.getOutputStream();
        this.server = connection.getContext().getServer();
        this.server.startExchange();
        this.tmpOutputStream = new BufferedOutputStream(this.rawOutputStream);
        this.uri = url;
    }

    public Headers getResponseHeaders() {
        return responseHeaders;
    }

    public Headers getRequestHeaders() {
        return requestHeaders;
    }

    public String getMethod() {
        return method;
    }

    public URI getUri() {
        return uri;
    }

    public HttpContext getContext() {
        return connection.getContext();
    }
    public boolean isHeadRequest() {
        return requestType.getFirst().equals(getMethod());
    }
    public boolean isGetRequest() {
        return requestType.get(1).equals(getMethod());
    }
    public boolean isPostRequest() {
        return requestType.get(2).equals(getMethod());
    }
    public boolean isPutRequest() {
        return requestType.get(3).equals(getMethod());
    }
    public boolean isDeleteRequest() {
        return requestType.get(4).equals(getMethod());
    }
    public boolean isNotifyRequest() {
        return requestType.get(5).equals(getMethod());
    }
    public boolean isSubscribeRequest() {
        return requestType.get(6).equals(getMethod());
    }
    public boolean isUnsubscribeRequest() {
        return requestType.getLast().equals(getMethod());
    }
    public int getResponseCode(){
        return code;
    }
    public void setResponseCode(int code){
        this.code = code;
    }

    public InetSocketAddress getLocalAddress(){
        Socket socket = connection.getSocketChannel().socket();
        InetAddress localAddress = socket.getLocalAddress();
        int localPort = socket.getLocalPort();
        return new InetSocketAddress(localAddress,localPort);
    }
    public InetSocketAddress getRemoteAddress(){
        Socket socket = connection.getSocketChannel().socket();
        InetAddress address = socket.getInetAddress();
        int port = socket.getPort();
        return new InetSocketAddress(address,port);
    }

    public String getProtocol(){
        String protocolLine = request.getProtocolLine();
        Matcher matcher = startLinePattern.matcher(protocolLine);
        if(!matcher.find()){
            throw new RuntimeException("未查询到协议行，请检查内部request初始化是否有误");
        }
        return matcher.group(3);
    }

    public HttpConnection getConnection(){
        return connection;
    }

    public ServerImpl getServer(){
        return getContext().getServer();
    }

    public static Exchange getExchange(HttpExchange exchange){
        if(exchange instanceof HttpExchangeImpl){
            return ((HttpExchangeImpl)exchange).getExchange();
        }
        throw new RuntimeException("并未实现其他的交换机，目前只有http交换机，如果需要其他类型的交换机，请自己实现");
    }

    public InputStream getRequestBody() {
        HttpEvent event = new HttpEvent(connection,"requestCompletionEvent", Event.EventType.REQUEST_COMPLETION);
        byte[] bytes = new byte[(int) this.requestContentLength];
        if(this.rawInputStream == null){
            throw new IllegalArgumentException("原始数据流为空！！！！");
        }
        if(this.rawInputStream instanceof Request.ReadStream readStream){
            if(readStream.isEof()){
                this.server.handlerEvent(event);
                return new ByteArrayInputStream(bytes);
            }
        }
        InputStream is = this.rawInputStream;

        try {
            if(this.requestContentLength>0){
                is.read(bytes,0, (int) this.requestContentLength);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        this.server.handlerEvent(event);
        return new ByteArrayInputStream(bytes);
    }

    public void sendResponse(String message) throws IOException {
        if(this.code == -1){
            this.code = Code.OK;
        }
        String protocolLine = "HTTP/1.1 "+Code.OK+" "+Code.getDescription(Code.OK)+"\r\n";
        byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
        int length = bytes.length;
        responseHeaders.add("Date",FORMATTER.format(LocalDateTime.now()));
        boolean b = responseHeaders.containsKey("Content-Type");
        System.out.println("responseHeaders.containsKey(\"Content-Type\"):"+responseHeaders.containsKey("Content-Type"));
        if(!b){
            responseHeaders.add("Content-Type", Configure.DEFAULT_RESPONSE_CONTENT_TYPE);
        }
        boolean b1 = responseHeaders.containsKey("HOST");
        if(!b1){
            Socket socket = connection.getSocketChannel().socket();
            InetSocketAddress remoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
            responseHeaders.add("HOST", remoteSocketAddress.getAddress().getHostAddress());
        }
        boolean b2 = responseHeaders.containsKey("Content-Length");
        if(!b2){
            responseHeaders.add("Content-Length",String.valueOf(length));
        }

        responseHeaders.add("Connection","close");

        ImmutableHeaders immutableHeaders = new ImmutableHeaders(responseHeaders);
        tmpOutputStream = new BufferedOutputStream(rawOutputStream);
        String headerString = immutableHeaders.getHeaderString();
        String s = protocolLine + headerString + message;
        System.out.println("----------------------发送的数据是------------------------");
        System.out.println(s);
        System.out.println("----------------------发送数据结束------------------------");
        tmpOutputStream.write(s.getBytes(StandardCharsets.UTF_8));
        tmpOutputStream.flush();
        responseHeaders.clear();
        HttpEvent event = new HttpEvent(connection,"responseCompletionEvent", Event.EventType.RESPONSE_COMPLETE);
        this.server.handlerEvent(event);

    }

}
